package com.arges.web.common;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.util.Assert;

/**
 * 用于将一个或多个对象里面的属性拷到另一个或多个对象内
 *
 * @author
 */
public class EnhancedBeanUtils extends BeanUtils
{

	private static final Logger logger = LoggerFactory.getLogger(EnhancedBeanUtils.class);

	/**
	 * 
	 * 当source里面的属性有值时（即value != null），才会将值拷到target的对应属性内
	 *
	 * @param sourceList
	 * @param targetList
	 * @since Ver 3.0
	 */
	@SuppressWarnings("unchecked")
	public static void copyPropertiesForSourceNotNullValues(List sourceList, List targetList)
	{
		if (sourceList.size() != targetList.size())
		{
			logger.error("无法拷贝对象,size different.");
		}

		for (int i = 0; i < sourceList.size(); i++)
		{
			copyPropertiesForSourceNotNullValues(sourceList.get(i), targetList.get(i), null, null);
		}
	}

	@SuppressWarnings("unchecked")
	public static void copyPorperties(List sourceList, List targetList, String... ignoreProperties)
	{
		if (sourceList.size() != targetList.size())
		{
			logger.error("无法拷贝对象,size different.");
		}

		for (int i = 0; i < sourceList.size(); i++)
		{
			BeanUtils.copyProperties(sourceList.get(i), targetList.get(i), ignoreProperties);
		}

	}

	@SuppressWarnings("unchecked")
	public static List createAndCopyToList(List sourceList, Class targetClass)
	{
		return createAndCopyToList(sourceList, targetClass, new String[] {});
	}

	@SuppressWarnings("unchecked")
	public static List createAndCopyToList(List sourceList, Class targetClass, String... ignorePropertis)
	{
		List targetList = new ArrayList();

		for (Object source : sourceList)
		{
			targetList.add(createAndCopyToObject(source, targetClass, ignorePropertis));
		}

		return targetList;
	}

	@SuppressWarnings("unchecked")
	public static Object createAndCopyToObject(Object source, Class targetClass)
	{
		return createAndCopyToObject(source, targetClass, new String[] {});
	}

	@SuppressWarnings("unchecked")
	public static Object createAndCopyToObject(Object source, Class targetClass, String... ignoreProperties)
	{
		Object target = null;
		try
		{
			target = targetClass.newInstance();
			copyPropertiesWithoutException(source, target, null, null);
		}
		catch (Exception e)
		{
			logger.error("copy object error.", e);
		}
		return target;
	}

	/**
	 * 
	 * 当source里面的属性有值时（即value != null），才会将值拷到target的对应属性内
	 *
	 * @param source
	 * @param target
	 * @since Ver 3.0
	 */
	public static void copyPropertiesForSourceNotNullValues(Object source, Object target)
	{
		copyPropertiesForSourceNotNullValues(source, target, null, null);
	}

	/*
	 * 从BeanUtils内拷贝过来 并稍微 做些修改
	 */
	private static void copyPropertiesForSourceNotNullValues(Object source, Object target, Class<?> editable,
			String[] ignoreProperties) throws BeansException
	{

		Assert.notNull(source, "Source must not be null");
		Assert.notNull(target, "Target must not be null");

		Class<?> actualEditable = target.getClass();
		if (editable != null)
		{
			if (!editable.isInstance(target))
			{
				throw new IllegalArgumentException("Target class [" + target.getClass().getName()
						+ "] not assignable to Editable class [" + editable.getName() + "]");
			}
			actualEditable = editable;
		}
		PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
		List<String> ignoreList = (ignoreProperties != null) ? Arrays.asList(ignoreProperties) : null;

		for (PropertyDescriptor targetPd : targetPds)
		{
			if (targetPd.getWriteMethod() != null
					&& (ignoreProperties == null || (!ignoreList.contains(targetPd.getName()))))
			{
				PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
				if (sourcePd != null && sourcePd.getReadMethod() != null)
				{
					try
					{
						Method readMethod = sourcePd.getReadMethod();
						if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers()))
						{
							readMethod.setAccessible(true);
						}
						Object value = readMethod.invoke(source);
						if (value != null)
						{
							Method writeMethod = targetPd.getWriteMethod();
							if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers()))
							{
								writeMethod.setAccessible(true);
							}
							writeMethod.invoke(target, value);
						}
					}
					catch (Throwable ex)
					{
						throw new FatalBeanException("Could not copy properties from source to target", ex);
					}
				}
			}
		}
	}

	private static void copyPropertiesWithoutException(Object source, Object target, Class<?> editable,
			String[] ignoreProperties)
	{
		Assert.notNull(source, "Source must not be null");
		Assert.notNull(target, "Target must not be null");

		Class<?> actualEditable = target.getClass();
		if (editable != null)
		{
			if (!editable.isInstance(target))
			{
				throw new IllegalArgumentException("Target class [" + target.getClass().getName()
						+ "] not assignable to Editable class [" + editable.getName() + "]");
			}
			actualEditable = editable;
		}
		PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
		List<String> ignoreList = (ignoreProperties != null) ? Arrays.asList(ignoreProperties) : null;

		for (PropertyDescriptor targetPd : targetPds)
		{
			if (targetPd.getWriteMethod() != null
					&& (ignoreProperties == null || (!ignoreList.contains(targetPd.getName()))))
			{
				PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
				if (sourcePd != null && sourcePd.getReadMethod() != null)
				{
					try
					{
						Method readMethod = sourcePd.getReadMethod();
						if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers()))
						{
							readMethod.setAccessible(true);
						}
						Object value = readMethod.invoke(source);
						Method writeMethod = targetPd.getWriteMethod();
						if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers()))
						{
							writeMethod.setAccessible(true);
						}
						writeMethod.invoke(target, value);
					}
					catch (Throwable ex)
					{
						// 不做处理
					}
				}
			}
		}
	}
}
